package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.binary.core.io.Resource;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.model.DataModuleRltClassDto;
import com.uinnova.product.eam.model.RltInfoQueryVo;
import com.uinnova.product.eam.model.VcCiRltInfo;
import com.uinnova.product.eam.model.asset.EamCiRltDTO;
import com.uinnova.product.eam.model.diagram.DiagramNodeLinkInfo;
import com.uinnova.product.eam.model.vo.ESRltSearchBeanVO;
import com.uinnova.product.eam.service.ICIRltSwitchSvc;
import com.uinnova.product.eam.service.es.IamsCIRltDesignNonComplianceDao;
import com.uinnova.product.eam.service.es.IamsCIRltPrivateNonComplianceDao;
import com.uinnova.product.eam.service.es.IamsESCIRltDesignSvc;
import com.uinnova.product.eam.service.es.IamsESCIRltPirvateSvc;
import com.uinnova.product.eam.service.utils.VisualModelUtils;
import com.uinnova.product.vmdb.comm.i18n.MessageUtil;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCiClass;
import com.uinnova.product.vmdb.comm.model.rlt.CcCiRlt;
import com.uinnova.product.vmdb.comm.util.CommUtil;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.db.eam.ESDiagramDao;
import com.uinnova.project.db.eam.ESDiagramLinkDao;
import com.uino.api.client.cmdb.ICIApiSvc;
import com.uino.api.client.cmdb.ICIRltApiSvc;
import com.uino.api.client.cmdb.IGraphAnalysisApiSvc;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.ESVisualModel;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.business.BindCiRltRequestDto;
import com.uino.bean.cmdb.business.ImportExcelMessage;
import com.uino.bean.cmdb.business.ImportResultMessage;
import com.uino.bean.cmdb.business.dataset.FriendInfo;
import com.uino.bean.cmdb.business.dataset.UpDownAttrCdt;
import com.uino.bean.cmdb.query.ESAttrAggBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.query.CSysUser;
import com.uino.dao.BaseConst;
import com.uino.dao.cmdb.ESCmdbCommSvc;
import com.uino.dao.cmdb.ESVisualModelSvc;
import com.uino.service.cmdb.dataset.microservice.IGraphAnalysisBase;
import com.uino.service.cmdb.microservice.ICIRltSvc;
import com.uino.service.permission.microservice.IUserSvc;
import com.uino.util.cache.ICacheService;
import com.uino.util.sys.SysUtil;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * CI关系管理，适配服务
 * 调用关系管理的相关方法时通过LibType切换(私有库/设计库/运行库）
 */
@Service
public class IamsCIRltSwitchSvc implements ICIRltSwitchSvc {

    @Autowired
    private ICIRltApiSvc iciRltApiSvc;

    @Autowired
    private IamsCIRltDesignSvc iamsCiRltDesignSvc;

    @Autowired
    private IamsCIRltPrivateSvc iamsCiRltPrivateSvc;

    @Autowired
    private IRltClassApiSvc rltClassApiSvc;

    @Autowired
    private IGraphAnalysisApiSvc graphAnalysisApiSvc;

    @Autowired
    private IGraphAnalysisBase graphAnalysisBase;

    @Autowired
    private ESCmdbCommSvc commSvc;

    @Autowired
    private ICIApiSvc ciSvc;

    @Autowired
    private IamsCISwitchSvc iamsCiSwitchSvc;
    @Autowired
    private IUserSvc userSvc;
    @Autowired
    private ESVisualModelSvc esVisualModelSvc;

    @Override
    public ICIRltApiSvc getCiRltApiSvc() {
        return iciRltApiSvc;
    }

    @Autowired
    private ICacheService iCacheService;

    @Autowired
    private IamsCIRltPrivateNonComplianceDao iamsCIRltPrivateNonComplianceDao;

    @Autowired
    private IamsCIRltDesignNonComplianceDao iamsCIRltDesignNonComplianceDao;

    @Autowired
    private IamsESCIRltDesignSvc iamsESCIRltDesignSvc;

    @Autowired
    private ESDiagramDao esDiagramDao;

    @Autowired
    private ESDiagramLinkDao esDiagramLinkDao;

    @Autowired
    private IamsESCIRltPirvateSvc iamsESCIRltPirvateSvc;

    // 自动成图缓存时间 默认一分钟
    @Value("${uino.auto.draw.cache.timeout:60000}")
    private Long catchTime;

    private static String AUTO_DRAW_KEY = "AUTO_DRAW_KEY:";

    /**
     * 获取私有库或设计库的Svc
     *
     * @param libType (PRIVATE 私有库, DESIGN 设计库）
     * @return ICIRltSvc
     */
    @Override
    public ICIRltSvc getCiRltSvc(LibType libType) {
        if (LibType.PRIVATE.equals(libType)) {
            return iamsCiRltPrivateSvc;
        } else {
            return iamsCiRltDesignSvc;
        }
    }

    @Override
    public Long bindCiRlt(BindCiRltRequestDto reqBean, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).bindCiRlt(reqBean);
        } else {
            return iciRltApiSvc.bindCiRlt(reqBean);
        }

    }

    @Override
    public ImportResultMessage bindCiRlts(Set<BindCiRltRequestDto> bindRltDtos, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).bindCiRlts(BaseConst.DEFAULT_DOMAIN_ID, bindRltDtos);
        } else {
            return iciRltApiSvc.bindCiRlts(bindRltDtos);
        }
    }

    @Override
    public Integer delRltByCiId(Long ciId, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).delRltByCiId(ciId);
        } else {
            return iciRltApiSvc.delRltByCiId(ciId);
        }
    }

    @Override
    public Integer delRltByIdsOrRltCodes(Set<Long> rltIds, Set<String> rltCodes, String ownerCode, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).delRltByIdsOrRltCodes(rltIds, rltCodes, ownerCode);
        } else {
            return iciRltApiSvc.delRltByIdsOrRltCodes(rltIds, rltCodes);
        }
    }

    @Override
    public Long updateCiRltAttr(Long ciRltId, Map<String, String> attrs, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).updateCiRltAttr(ciRltId, attrs);
        } else {
            return iciRltApiSvc.updateCiRltAttr(ciRltId, attrs);
        }
    }

    @Override
    public Page<CcCiRltInfo> searchRltByBean(ESRltSearchBean bean, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).searchRltByBean(bean);
        } else {
            return iciRltApiSvc.searchRltByBean(bean);
        }
    }

    @Override
    public List<CcCiRltInfo> searchRltByIds(Set<Long> ids, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).searchRltByIds(ids);
        } else {
            return iciRltApiSvc.searchRltByIds(ids);
        }
    }

    @Override
    public Integer clearRltByClassId(Long rltClassId, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).clearRltByClassId(rltClassId);
        } else {
            return iciRltApiSvc.clearRltByClassId(rltClassId);
        }
    }

    @Override
    public Integer clearUserRltByClassId(Long rltClassId, String ownerCode, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).clearUserRltByClassId(rltClassId, ownerCode);
        } else {
            return iciRltApiSvc.clearRltByClassId(rltClassId);
        }
    }

    @Override
    public Resource exportCiRlt(Set<Long> rltClassIds, Set<Long> rltIds, LibType libType, String ownerCode) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).exportCiRlt(rltClassIds, rltIds, ownerCode);
        } else {
            return iciRltApiSvc.exportCiRlt(rltClassIds, rltIds);
        }
    }

    @Override
    public ImportResultMessage importCiRlt(String excelFilePath, MultipartFile excelFile, Set<String> rltClsCodes, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).importCiRlt(BaseConst.DEFAULT_DOMAIN_ID, excelFilePath, excelFile, rltClsCodes);
        } else {
            return iciRltApiSvc.importCiRlt(excelFilePath, excelFile, rltClsCodes);
        }
    }

    @Override
    public Map<String, Boolean> comprehendRltExcel(MultipartFile excelFile, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).comprehendRltExcel(excelFile);
        } else {
            return iciRltApiSvc.comprehendRltExcel(excelFile);
        }
    }

    @Override
    public ImportExcelMessage parseRltExcel(String excelFilePath, MultipartFile excelFile, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).parseRltExcel(excelFilePath, excelFile);
        } else {
            return iciRltApiSvc.parseRltExcel(excelFilePath, excelFile);
        }
    }

    @Override
    public Page<ESCIRltInfo> searchRlt(ESRltSearchBean bean, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).searchRlt(bean);
        } else {
            return iciRltApiSvc.searchRlt(bean);
        }
    }

    @Override
    public List<ESCIRltInfo> searchRlt(Set<Long> classIds, Set<Long> sourceClassIds, Set<Long> targetClassIds, LibType libType) {
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageSize(1);
        bean.setPageSize(10000);
        bean.setRltClassIds(Lists.newArrayList(classIds));
        bean.setSourceClassIds(Lists.newArrayList(sourceClassIds));
        bean.setTargetClassIds(Lists.newArrayList(targetClassIds));
        Page<ESCIRltInfo> resultPage;
        if (!LibType.BASELINE.equals(libType)) {
            resultPage = getCiRltSvc(libType).searchRlt(bean);
        } else {
            resultPage = iciRltApiSvc.searchRlt(bean);
        }
        return CollectionUtils.isEmpty(resultPage.getData())?Collections.emptyList(): resultPage.getData();
    }

    @Override
    public Page<String> groupByField(ESAttrAggBean req, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).groupByField(BaseConst.DEFAULT_DOMAIN_ID, req);
        } else {
            return iciRltApiSvc.groupByField(req);
        }
    }

    @Override
    public Map<Long, Map<Long, Set<Long>>> getClassRltMapByClsQuery(Set<Long> clsIds, Set<Long> rltClsIds, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).getClassRltMapByClsQuery(clsIds, rltClsIds);
        } else {
            return iciRltApiSvc.getClassRltMapByClsQuery(clsIds, rltClsIds);
        }
    }

    @Override
    public List<DataModuleRltClassDto> getClassRltList(Set<Long> classIds, Set<Long> rltClsIds, LibType libType) {
        // 查询分类关系{源分类id:{关系分类id:[目标分类ids]}}
        Map<Long, Map<Long, Set<Long>>> allRltMap = this.getClassRltMapByClsQuery(classIds, rltClsIds, libType);

        // 获取到所有的关系分类ID
        Set<Long> rltClsIdsInResult = new HashSet<>();
        allRltMap.forEach((allRltMapKey, rltClsTargetCiClsMap) -> rltClsIdsInResult.addAll(rltClsTargetCiClsMap.keySet()));

        // 查询到关系分类列表
        CCcCiClass cdt = new CCcCiClass();
        cdt.setIds(rltClsIdsInResult.toArray(new Long[0]));
        List<CcCiClassInfo> rltClassInfos = rltClassApiSvc.getRltClassByCdt(cdt);
        Map<Long, CcCiClassInfo> rltClassMap = new HashMap<>();
        for (CcCiClassInfo rltClassInfo : rltClassInfos) {
            rltClassMap.put(rltClassInfo.getCiClass().getId(), rltClassInfo);
        }
        List<DataModuleRltClassDto> retList = new LinkedList<>();
        allRltMap.forEach((allRltMapKey, rltClsTargetCiClsMap) ->
                rltClsTargetCiClsMap.forEach((rltClassId, targetClassIdSet) -> {
                    for (long targetClassId : targetClassIdSet) {
                        DataModuleRltClassDto dataModuleRltClassDto = new DataModuleRltClassDto();
                        dataModuleRltClassDto.setSourceClassId(allRltMapKey);
                        dataModuleRltClassDto.setTargetClassId(targetClassId);
                        dataModuleRltClassDto.setRltClassInfo(rltClassMap.get(rltClassId));
                        retList.add(dataModuleRltClassDto);
                    }
                }));
        return retList;
    }

    @Override
    public List<VcCiRltInfo> queryUpAndDownRlt(LibType libType, Long sCiId, List<Long> targetCiClassIds, List<Long> rltClassIds, Integer up, Integer down, Boolean isCache) {
        // 缓存数据默认有60秒延迟，暂时只可以在自动成图功能使用
        if (isCache) {
            // 在redis查询是否存在缓存数据
            Object obj = iCacheService.getCache(AUTO_DRAW_KEY + sCiId);
            if (!BinaryUtils.isEmpty(obj)) {
                List<ESCIRltInfo> vcCiRltInfos = JSON.parseObject(obj.toString(), new TypeReference<List<ESCIRltInfo>>() {});
                return converCIRlt(vcCiRltInfos);
            }
        }
        up = up == null ? 0 : up;
        down = down == null ? 0 : down;
        MessageUtil.checkEmpty(sCiId, "sCiId");
        List<UpDownAttrCdt> rltConditions = new ArrayList<>();
        if (rltClassIds != null && rltClassIds.size() > 0) {
            for (Long rltClassId : rltClassIds) {
                UpDownAttrCdt attrCdt = new UpDownAttrCdt();
                attrCdt.setClassId(rltClassId);
                rltConditions.add(attrCdt);
            }
        }
        List<UpDownAttrCdt> ciConditions = new ArrayList<>();
        if(targetCiClassIds !=null && targetCiClassIds.size() > 0){
            for (Long targetCiClassId : targetCiClassIds) {
                UpDownAttrCdt cdt = new UpDownAttrCdt();
                cdt.setClassId(targetCiClassId);
                ciConditions.add(cdt);
            }
        }
        //支持私有库和运行库
        FriendInfo friendInfo = queryCiUpDownByCiId(1L, sCiId, ciConditions, rltConditions,
                new ArrayList<>(), up, down, true, libType);
        List<ESCIRltInfo> rltInfos = friendInfo.getCiRltLines();
        // 将返回结果存入redis
        if (isCache) {
            iCacheService.setCache(AUTO_DRAW_KEY + sCiId, JSON.toJSONString(rltInfos), catchTime);
        }
        return converCIRlt(rltInfos);
    }

    /**
     *  返回值转化关系
     * @param rltInfos
     * @return
     */
    public List<VcCiRltInfo> converCIRlt (List<ESCIRltInfo> rltInfos) {
        List<CcCiRltInfo> ccCiRltInfos = new ArrayList<>();
        if (rltInfos.size() > 0) {
            rltInfos.forEach(rltInfo -> ccCiRltInfos.add(commSvc.tranCcCiRltInfo(rltInfo, false)));
        }
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        Set<Long> ciIds = new HashSet<>();
        for (CcCiRltInfo ccCiRltInfo : ccCiRltInfos) {
            CcCiRlt ciRlt = ccCiRltInfo.getCiRlt();
            ciIds.add(ciRlt.getSourceCiId());
            ciIds.add(ciRlt.getTargetCiId());
        }
        Map<Long, CcCiInfo> ciMap = new HashMap<Long, CcCiInfo>();
        if (ciIds.size() > 0) {
            CCcCi cdt = new CCcCi();
            cdt.setIds(ciIds.toArray(new Long[]{}));
            cdt.setDomainId(domainId);
            List<CcCiInfo> ciInfos = ciSvc.queryCiInfoList(domainId, cdt, null, false, false);
            for (CcCiInfo ccCiInfo : ciInfos) {
                ciMap.put(ccCiInfo.getCi().getId(), ccCiInfo);
            }
        }
        List<VcCiRltInfo> ret = CommUtil.copy(ccCiRltInfos, VcCiRltInfo.class);
        for (VcCiRltInfo ccCiRltInfo : ret) {
            CcCiRlt ciRlt = ccCiRltInfo.getCiRlt();

            ccCiRltInfo.setSourceCiInfo(ciMap.get(ciRlt.getSourceCiId()));
            ccCiRltInfo.setTargetCiInfo(ciMap.get(ciRlt.getTargetCiId()));
        }
        return ret;
    }

    @Override
    public FriendInfo queryCiUpDownByCiId(Long domainId, Long startCiId, List<UpDownAttrCdt> ciConditions, List<UpDownAttrCdt> rltConditions,
                                          List<Long> rltLvls, Integer upLevel, Integer downLevel, Boolean hasAttr, LibType libType) {
        if (LibType.BASELINE.equals(libType)) {
            return graphAnalysisApiSvc.queryCiUpDownByCiId(domainId, startCiId, ciConditions, rltConditions,
                    rltLvls, upLevel, downLevel, hasAttr);
        } else {
            return graphAnalysisBase.queryCiUpDownByCiId(domainId, startCiId, ciConditions, rltConditions,
                    rltLvls, upLevel, downLevel, hasAttr, iamsCiSwitchSvc.getCiSvc(libType), getCiRltSvc(libType));
        }
    }

    @Override
    public List<EamCiRltDTO> searchRltByRltCodes(ESRltSearchBean reqBean, LibType libType) {
        // 后端设置查询500条关系数据上限
        reqBean.setPageNum(1);
        reqBean.setPageSize(500);
        Page<CcCiRltInfo> page = searchRltByBean(reqBean, libType);
        // 查询所属分类
        Set<Long> classIds = page.getData().stream().map(ccCiRltInfo -> ccCiRltInfo.getCiRlt().getClassId()).collect(Collectors.toSet());

        CCcCiClass cdt = new CCcCiClass();
        cdt.setIds(classIds.toArray(new Long[0]));
        List<CcCiClassInfo> rltClassList = rltClassApiSvc.getRltClassByCdt(cdt);
        Map<Long, CcCiClassInfo> rltClassMap = new HashMap<>();
        rltClassList.forEach(classInfo -> rltClassMap.put(classInfo.getCiClass().getId(), classInfo));

        List<String> sourceCiCodes = page.getData().stream().map(CcCiRltInfo::getCiRlt).map(CcCiRlt::getSourceCiCode).collect(Collectors.toList());
        List<String> targetCiCodes = page.getData().stream().map(CcCiRltInfo::getCiRlt).map(CcCiRlt::getTargetCiCode).collect(Collectors.toList());

        Set<String> ciCodes = new HashSet<>();
        ciCodes.addAll(sourceCiCodes);
        ciCodes.addAll(targetCiCodes);
        CCcCi ciCdt = new CCcCi();
        if(LibType.PRIVATE == libType){
            ciCdt.setOwnerCodeEqual(reqBean.getOwnerCode());
        }
        ciCdt.setCiCodes(ciCodes.toArray(new String[ciCodes.size()]));
        List<CcCiInfo> ciInfos = iamsCiSwitchSvc.queryCiInfoList(reqBean.getDomainId(), ciCdt, "id", true, false, libType);
        Map<String, CcCiInfo> ciCodeCiMap = ciInfos.stream().collect(Collectors.toMap(each -> each.getCi().getCiCode(), each -> each, (k1, k2) -> k1));

        List<EamCiRltDTO> result = new ArrayList<>();

        for (CcCiRltInfo rltInfo : page.getData()) {
            EamCiRltDTO rltDTO = EamUtil.copy(rltInfo, EamCiRltDTO.class);
            CcCiInfo sourceCiInfo = ciCodeCiMap.get(rltDTO.getCiRlt().getSourceCiCode());
            CcCiInfo targetCiInfo = ciCodeCiMap.get(rltDTO.getCiRlt().getSourceCiCode());
            if(!BinaryUtils.isEmpty(sourceCiInfo)){
                rltDTO.setSourceCiInfo(sourceCiInfo);
                rltDTO.getCiRlt().setSourceCiCode(sourceCiInfo.getCi().getCiCode());
                rltDTO.getCiRlt().setSourceCiId(sourceCiInfo.getCi().getId());
            }
            if(!BinaryUtils.isEmpty(targetCiInfo)){
                rltDTO.setTargetCiInfo(targetCiInfo);
                rltDTO.getCiRlt().setTargetCiCode(targetCiInfo.getCi().getCiCode());
                rltDTO.getCiRlt().setTargetCiId(targetCiInfo.getCi().getId());
            }
            rltDTO.setCiClass(rltClassMap.get(rltInfo.getCiRlt().getClassId()).getCiClass());
            result.add(rltDTO);
        }
        Set<EamCiRltDTO> esciRltInfoTreeSet = new TreeSet<EamCiRltDTO>(Comparator.comparing(eamCiRltDTO -> eamCiRltDTO.getCiRlt().getCiCode()));
        esciRltInfoTreeSet.addAll(result);
        return new ArrayList<>(esciRltInfoTreeSet);
    }

    @Override
    public List<CcCiRltInfo> searchRltByRltUniqueCodesWithoutCi(RltInfoQueryVo rltInfoQueryVo) {
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageNum(1);
        bean.setPageSize(1000);
        bean.setRltUniqueCodes(rltInfoQueryVo.getRltUniqueCodes());
        if (rltInfoQueryVo.getLibType() == LibType.PRIVATE) {
            bean.setOwnerCode(SysUtil.getCurrentUserInfo().getLoginCode());
        }
        Page<ESCIRltInfo> page = this.getCiRltSvc(rltInfoQueryVo.getLibType()).searchRlt(bean);
        List<CcCiRltInfo> ccCiRltInfos = new ArrayList<>();
        if (CollectionUtils.isEmpty(page.getData())) {
            return ccCiRltInfos;
        }
        List<ESCIRltInfo> esciRltInfos = page.getData();
        Map<String, SysUser> userMap = new HashMap<>();
        if (rltInfoQueryVo.getLibType() == LibType.PRIVATE) {
            Set<String> userLoginCodes = esciRltInfos.stream()
                    .filter(rlt -> rlt.getOwnerCode() != null)
                    .map(ESCIRltInfo::getOwnerCode).collect(Collectors.toSet());
            CSysUser user = new CSysUser();
            user.setLoginCodes(userLoginCodes.toArray(new String[userLoginCodes.size()]));
            List<SysUser> sysUserByCdt = userSvc.getSysUserByCdt(user);
            userMap = sysUserByCdt.stream().collect(Collectors.toMap(SysUser::getLoginCode, each -> each));
        }
        for (ESCIRltInfo esciRltInfo : esciRltInfos) {
            CcCiRltInfo ccCiRltInfo = new CcCiRltInfo();
            ccCiRltInfo.setAttrs(esciRltInfo.getAttrs());
            esciRltInfo.setAttrs(null);
            ccCiRltInfo.setCiRlt(esciRltInfo);
            this.fillBelongUserCasePrivateRlt(ccCiRltInfo, rltInfoQueryVo.getLibType(), userMap);
            ccCiRltInfos.add(ccCiRltInfo);
        }
        return ccCiRltInfos;
    }

    private void fillBelongUserCasePrivateRlt(CcCiRltInfo ccCiRltInfo, LibType libType, Map<String, SysUser> userMap) {
        if (libType != LibType.PRIVATE) {
            return;
        }
        Map<String, String> attrs = ccCiRltInfo.getAttrs();
        if (attrs == null) {
            attrs = new HashMap<>();
            ccCiRltInfo.setAttrs(attrs);
        }
        String userName = "admin";
        SysUser currentUser = userMap.get(ccCiRltInfo.getCiRlt().getOwnerCode());
        if (!BinaryUtils.isEmpty(currentUser)) {
            userName = currentUser.getUserName() + "(" + currentUser.getLoginCode() + ")";
        }
        attrs.put("所属用户", userName);
    }


    @Override
    public List<CcCiClassInfo> queryAllClasses(LibType libType) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).queryAllClasses(domainId);    //设计库/私有库
        } else {
            return iciRltApiSvc.queryAllClasses(domainId);  //运行库
        }
    }

    @Override
    public List<SysUser> findCiRltUserList(ESRltSearchBean reqBean) {
        return iamsCiRltPrivateSvc.findCiRltUserList(reqBean);
    }


    @Override
    public List<CcCiRltInfo> queryCiBetWeenRlt(List<String> ciCodes) {
        //设计库设计库关系源端的关系
        List<CcCiRltInfo> existSourceRlt = Collections.emptyList();
        if (CollectionUtils.isEmpty(ciCodes)) {
            return existSourceRlt;
        }
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setSourceCiCodes(Sets.newHashSet(ciCodes));
        Page<CcCiRltInfo> sourceCiRltPage = searchRltByBean(bean, LibType.DESIGN);
        if (!CollectionUtils.isEmpty(sourceCiRltPage.getData())) {
            //对象作为源端的存在，验证目标端是否在视图中存在
            List<CcCiRltInfo> sourceCIRltList = sourceCiRltPage.getData();
            existSourceRlt = sourceCIRltList.stream()
                    .filter(ccCiRltInfo -> ciCodes.contains(ccCiRltInfo.getTargetCiInfo().getCi().getCiCode())).collect(Collectors.toList());
        }
        return existSourceRlt;
    }

    @Override
    public List<ESCIRltInfo> bindCiRltBatch(List<BindCiRltRequestDto> reqBean, LibType libType) {
        if (LibType.PRIVATE.equals(libType)) {
            return iamsCiRltPrivateSvc.bindBatchCiRlt(reqBean);
        } else {
            return Collections.emptyList();
        }
    }

    @Override
    public CcCiClassInfo getRltClassByCode(String rltClassCode) {
        SysUser currentUserInfo = SysUtil.getCurrentUserInfo();
        CCcCiClass cdt = new CCcCiClass();
        cdt.setDomainId(currentUserInfo.getDomainId());
        cdt.setClassCodeEqual(rltClassCode);
        List<CcCiClassInfo> rltClassByCdt = rltClassApiSvc.getRltClassByCdt(cdt);
        if (CollectionUtils.isEmpty(rltClassByCdt)) {
            return new CcCiClassInfo();
        }
        return rltClassByCdt.get(0);
    }

    @Override
    public List<CcCiRltInfo> getRltInfoByClassId(List<Long> rltClassId, Long sourceClassId, Long targetClassId, String ownerCode, LibType libType) {
        ESRltSearchBean rltSearchBean = new ESRltSearchBean();
        if (LibType.PRIVATE.equals(libType)) {
            rltSearchBean.setOwnerCode(ownerCode);
        }
        rltSearchBean.setPageNum(1);
        rltSearchBean.setPageSize(10000);
        rltSearchBean.setRltClassIds(rltClassId);
        rltSearchBean.setSourceClassIds(Collections.singletonList(sourceClassId));
        rltSearchBean.setTargetClassIds(Collections.singletonList(targetClassId));
        Page<CcCiRltInfo> rltInfoPage = searchRltByBean(rltSearchBean, libType);
        return CollectionUtils.isEmpty(rltInfoPage.getData())?Collections.emptyList():rltInfoPage.getData();
    }

    @Override
    public List<CcCiRltInfo> getRltByCode(String rltCode, String ownerCode, LibType libType) {
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setRltCodes(Sets.newHashSet(rltCode));
        if(LibType.PRIVATE.equals(libType) && !BinaryUtils.isEmpty(ownerCode)){
            bean.setOwnerCode(ownerCode);
        }
        Page<CcCiRltInfo> resultPage;
        if (!LibType.BASELINE.equals(libType)) {
            resultPage = getCiRltSvc(libType).searchRltByBean(bean);
        } else {
            resultPage = iciRltApiSvc.searchRltByBean(bean);
        }
        if(BinaryUtils.isEmpty(resultPage.getData())){
            return Collections.emptyList();
        }
        return resultPage.getData();
    }

    @Override
    public List<CcCiRltInfo> searchRltByScroll(ESRltSearchBean rltSearchBean, LibType libType) {
        if (!LibType.BASELINE.equals(libType)) {
            return getCiRltSvc(libType).searchRltByScroll(rltSearchBean);
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    public List<ESCIRltInfo> getRltByUniqueCodes(Set<String> uniqueCodes, String ownerCode, LibType libType) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageNum(1);
        bean.setPageSize(10000);
        bean.setDomainId(domainId);
        if(LibType.PRIVATE.equals(libType) && !BinaryUtils.isEmpty(ownerCode)){
            bean.setOwnerCode(ownerCode);
        }
        bean.setRltUniqueCodes(uniqueCodes);
        Page<ESCIRltInfo> rltPage = searchRlt(bean, libType);
        return BinaryUtils.isEmpty(rltPage.getData())?Collections.emptyList():rltPage.getData();
    }

    @Override
    public List<CcCiClassInfo> queryClassesByRltClassIds(List<Long> rltClassIds) {
        if (CollectionUtils.isEmpty(rltClassIds)) {
            return new ArrayList<>();
        }
        CCcCiClass cdt = new CCcCiClass();
        cdt.setIds(rltClassIds.toArray(new Long[rltClassIds.size()]));
        return rltClassApiSvc.getRltClassByCdt(cdt);
    }

    @Override
    public boolean findNonCompliance(Long rltClassId,LibType libType) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("classId", rltClassId));
        List<ESCIRltInfo> returnVals = new LinkedList<>();
        long diagramCount = 0l;
        BoolQueryBuilder boolQueryDiagram = QueryBuilders.boolQuery();
        if (LibType.PRIVATE.equals(libType)) {
            long count = iamsESCIRltPirvateSvc.countByCondition(boolQueryBuilder);
            if (count > 0) {
                //统计出查询指定分类下面的关系数据 通过rltClassId
                Map<String, Page<ESCIRltInfo>> rltInfoMap = iamsESCIRltPirvateSvc.getScrollByQuery(1, 3000, boolQueryBuilder, "id", true);
                String scrollId = rltInfoMap.keySet().iterator().next();
                if (rltInfoMap.get(scrollId).getData() != null && rltInfoMap.get(scrollId).getData().size() > 0) {
                    returnVals.addAll(rltInfoMap.get(scrollId).getData());
                }
                if (rltInfoMap.get(scrollId).getTotalRows() > 3000) {
                    while (true) {
                        List<ESCIRltInfo> nextResults = iamsESCIRltPirvateSvc.getListByScroll(scrollId);
                        if (nextResults != null && nextResults.size() > 0) {
                            returnVals.addAll(nextResults);
                        } else {
                            break;
                        }
                    }
                }
                //下面查询所有没有发布的视图
                boolQueryDiagram.must(QueryBuilders.termQuery("dataStatus", 1));
                boolQueryDiagram.must(QueryBuilders.termQuery("status", 1));
                boolQueryDiagram.must(QueryBuilders.termQuery("isOpen", 0));
                diagramCount = esDiagramDao.countByCondition(boolQueryDiagram);
            }else{
                return true;
            }
        } else if (LibType.DESIGN.equals(libType)) {
            long count = iamsESCIRltDesignSvc.countByCondition(boolQueryBuilder);
            //统计出查询指定分类下面的关系数据 通过rltClassId
            if (count > 0) {
                Map<String, Page<ESCIRltInfo>> rltInfoMap = iamsESCIRltDesignSvc.getScrollByQuery(1, 3000, boolQueryBuilder, "id", true);
                String scrollId = rltInfoMap.keySet().iterator().next();
                if (rltInfoMap.get(scrollId).getData() != null && rltInfoMap.get(scrollId).getData().size() > 0) {
                    returnVals.addAll(rltInfoMap.get(scrollId).getData());
                }
                if (rltInfoMap.get(scrollId).getTotalRows() > 3000) {
                    while (true) {
                        List<ESCIRltInfo> nextResults = iamsESCIRltDesignSvc.getListByScroll(scrollId);
                        if (nextResults != null && nextResults.size() > 0) {
                            returnVals.addAll(nextResults);
                        } else {
                            break;
                        }
                    }
                }
                //下面查询所有已发布到资产库里面的视图

                boolQueryDiagram.must(QueryBuilders.termQuery("dataStatus", 1));
                boolQueryDiagram.must(QueryBuilders.termQuery("status", 1));
                boolQueryDiagram.must(QueryBuilders.termQuery("isOpen", 1));
                boolQueryDiagram.must(QueryBuilders.termQuery("historyVersionFlag", 1));
                diagramCount = esDiagramDao.countByCondition(boolQueryDiagram);
            }else{
                return true;
            }
        }

        if (diagramCount > 0) {
            //计算需要线程数
            int threadCount = getThreadCount(diagramCount);
            final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
            ConcurrentLinkedQueue<List<String>> allCiCodeQueue = new ConcurrentLinkedQueue<>();
            ExecutorService threadPool = getThreadPool();
            Map<String, Page<ESDiagram>> diagramMap = esDiagramDao.getScrollByQuery(1, 3000, boolQueryDiagram, "id", true);
            String scrollId = diagramMap.keySet().iterator().next();
            if (diagramMap.get(scrollId).getData() != null && diagramMap.get(scrollId).getData().size() > 0) {
                List<ESDiagram> firstDiagramList = diagramMap.get(scrollId).getData();
                //调用线程池执行线程
                threadPool.submit(new CiRltTask(allCiCodeQueue, returnVals, firstDiagramList, esDiagramLinkDao,countDownLatch));
            }
            if (diagramMap.get(scrollId).getTotalRows() > 3000) {
                while (true) {
                    List<ESDiagram> diagramNextResult = esDiagramDao.getListByScroll(scrollId);
                    if (diagramNextResult != null && diagramNextResult.size() > 0) {
                        //调用线程池执行线程
                        threadPool.submit(new CiRltTask(allCiCodeQueue, returnVals, diagramNextResult, esDiagramLinkDao,countDownLatch));
                    } else {
                        break;
                    }
                }
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadPool.shutdown();
            Set<String> collect = allCiCodeQueue.stream().flatMap(List::stream).distinct().collect(Collectors.toSet());
            returnVals.removeIf(item -> collect.contains(item.getUniqueCode()));
            //合规检查的数据入库之前应该清除之前的数据 按照rltClassId
            BoolQueryBuilder boolQueryNonBuilder = QueryBuilders.boolQuery();
            boolQueryNonBuilder.must(QueryBuilders.termQuery("classId",rltClassId));
            if (LibType.DESIGN.equals(libType)) {
                iamsCIRltDesignNonComplianceDao.deleteByQuery(boolQueryNonBuilder,true);
                List<List<ESCIRltInfo>> chunks = new ArrayList<List<ESCIRltInfo>>();
                for (int i = 0; i < returnVals.size(); i += 3000) {
                    chunks.add(returnVals.subList(i, Math.min(i + 3000, returnVals.size())));
                }
                for (List<ESCIRltInfo> chunk : chunks) {
                    iamsCIRltDesignNonComplianceDao.saveOrUpdateBatch(chunk);
                }
            } else if (LibType.PRIVATE.equals(libType)) {
                iamsCIRltPrivateNonComplianceDao.deleteByQuery(boolQueryNonBuilder,true);
                List<List<ESCIRltInfo>> chunks = new ArrayList<List<ESCIRltInfo>>();
                for (int i = 0; i < returnVals.size(); i += 3000) {
                    chunks.add(returnVals.subList(i, Math.min(i + 3000, returnVals.size())));
                }
                for (List<ESCIRltInfo> chunk : chunks) {
                    iamsCIRltPrivateNonComplianceDao.saveOrUpdateBatch(chunk);
                }
            }
            return true;
        }
        return true;
    }

    @Override
    public Page<CcCiRltInfo> searchRltByBeanVO(ESRltSearchBeanVO bean, LibType libType) {
        if (LibType.PRIVATE.equals(libType)) {
            return iamsCiRltPrivateSvc.searchRltByBeanVO(bean);
        } else if(LibType.DESIGN.equals(libType)){
            return iamsCiRltDesignSvc.searchRltByBeanVO(bean);

        }else{
            return iciRltApiSvc.searchRltByBean(bean);
        }
    }

    @Override
    public Resource exportCiRltByConditions(ESRltSearchBeanVO reqBean, LibType libType, String ownerCode) {
        if (LibType.PRIVATE.equals(libType)) {
            return iamsCiRltPrivateSvc.exportCiRltByConditions(reqBean, ownerCode);
        } else if (LibType.DESIGN.equals(libType)) {
            return iamsCiRltDesignSvc.exportCiRltByConditions(reqBean, ownerCode);
        } else {
            return iciRltApiSvc.exportCiRlt(new HashSet<>(reqBean.getRltClassIds()) , reqBean.getRltId());
        }
    }

    private int getThreadCount(long diagramCount) {
        if (diagramCount % 3000l == 0) {
            return (int)diagramCount / 3000;
        }else{
            return (int) (diagramCount/3000l)+1;
        }
    }

    //自定义线程池
    private ExecutorService getThreadPool() {
        // 设置线程池参数
        int corePoolSize = 2;
        int maximumPoolSize = 2;
        int maxQueueSize = 100;
        long keepAliveTime = 0L;
        TimeUnit unit = TimeUnit.MILLISECONDS;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(maxQueueSize);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        // 创建线程池
        return new ThreadPoolExecutor(
                corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    public List<CcCiClass> filterByVisualModel(Long sourceClassId, Long targetClassId) {
        //1.获取当前使用的元模型信息
        ESVisualModel esVisualModel = esVisualModelSvc.getEnableModel(SysUtil.getCurrentUserInfo().getDomainId());
        if (BinaryUtils.isEmpty(esVisualModel.getJson())) {
            return Collections.emptyList();
        }
        //获取元模型中存在的ClassId
        Set<Long> rltClassIds = new HashSet<>();
        List<DiagramNodeLinkInfo> rltLinkList = VisualModelUtils.getRltClassIds(esVisualModel);
        for (DiagramNodeLinkInfo link : rltLinkList) {
            List<Long> classIds = Lists.newArrayList(link.getSourceId(), link.getTargetId());
            if(classIds.contains(sourceClassId) && classIds.contains(targetClassId)){
                rltClassIds.add(link.getLinkId());
            }
        }
        if (BinaryUtils.isEmpty(rltClassIds)) {
            return Collections.emptyList();
        }

        CCcCiClass cdt = new CCcCiClass();
        cdt.setIds(rltClassIds.toArray(new Long[0]));
        List<CcCiClassInfo> rltClassList = rltClassApiSvc.getRltClassByCdt(cdt);
        if(CollectionUtils.isEmpty(rltClassList)){
            return Collections.emptyList();
        }
        return rltClassList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toList());
    }

    @Override
    public List<CcCiRltInfo> queryRltByClassIds(Set<Long> rltIdList, Set<Long> sourceIdList, Set<Long> targetIdList) {
        return this.queryRltByClassIds(rltIdList, sourceIdList, targetIdList, null, LibType.DESIGN);
    }

    @Override
    public List<CcCiRltInfo> queryRltByClassIds(Set<Long> rltIdList, Set<Long> sourceIdList, Set<Long> targetIdList, String ownerCode, LibType libType) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageSize(1);
        bean.setPageSize(10000);
        bean.setDomainId(domainId);
        bean.setRltClassIds(Lists.newArrayList(rltIdList));
        bean.setSourceClassIds(Lists.newArrayList(sourceIdList));
        bean.setTargetClassIds(Lists.newArrayList(targetIdList));
        if(LibType.PRIVATE.equals(libType) && !BinaryUtils.isEmpty(ownerCode)){
            bean.setOwnerCode(ownerCode);
        }
        Page<CcCiRltInfo> rltInfoPage = this.searchRltByBean(bean, libType);
        List<CcCiRltInfo> data = rltInfoPage.getData();
        if(BinaryUtils.isEmpty(rltInfoPage.getData())){
            return Collections.emptyList();
        }
        return rltInfoPage.getData();
    }

    @Override
    public List<CcCiRltInfo> queryRltByCodes(Set<String> sourceCiCodes, Set<String> targetCiCodes, List<Long> sourceClassIds, List<Long> targetClassIds, String ownerCode, LibType libType) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageSize(1);
        bean.setPageSize(10000);
        bean.setDomainId(domainId);
        if(LibType.PRIVATE.equals(libType) && !BinaryUtils.isEmpty(ownerCode)){
            bean.setOwnerCode(ownerCode);
        }
        if(!CollectionUtils.isEmpty(sourceCiCodes)){
            bean.setSourceCiCodes(sourceCiCodes);
        }
        if(!CollectionUtils.isEmpty(targetCiCodes)){
            bean.setTargetCiCodes(targetCiCodes);
        }
        if(!CollectionUtils.isEmpty(sourceClassIds)){
            bean.setSourceClassIds(sourceClassIds);
        }
        if(!CollectionUtils.isEmpty(targetClassIds)){
            bean.setTargetClassIds(targetClassIds);
        }
        Page<CcCiRltInfo> rltInfoPage = this.searchRltByBean(bean, libType);
        if(BinaryUtils.isEmpty(rltInfoPage.getData())){
            return Collections.emptyList();
        }
        return rltInfoPage.getData();
    }

    @Override
    public List<CcCiRltInfo> queryRltByCodes(Set<String> sourceCiCodes, Set<String> targetCiCodes, List<Long> sourceClassIds, List<Long> targetClassIds) {
        return this.queryRltByCodes(sourceCiCodes, targetCiCodes, sourceClassIds, targetClassIds, null, LibType.DESIGN);
    }
}
